/**
 * @file bit-field.h
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2024-03-30
 * 
 * @copyright Copyright (c) 2024
 * 
 */
#ifndef __JSASM_BASE_FIELD_H__
#define __JSASM_BASE_FIELD_H__

#include <stdint.h>
#include "include/jsasm_config.h"

namespace base {

    template <class T, int shift, int size, class U = uint32_t>
    class BitField final {
        public:
            static_assert(std::is_unsigned<U>::value);
            static_assert(shift < 8 * sizeof(U));
            static_assert(size < 8 * sizeof(U));
            static_assert(shift + size <= 8 * sizeof(U));
            static_assert(size > 0);

            using FieldType = T;

            static constexpr int kShift = shift;
            static constexpr int kSize = size;
            static constexpr U kMask = ((U{1} << kShift) << kSize) - (U{1} << kShift);
            static constexpr int kLastUsedBit = kShift + kSize - 1;
            static constexpr U kNumValues = U{1} << kSize;

            static constexpr T kMax = static_cast<T>(kNumValues - 1);

            template <class T2, int size2>
            using Next = BitField<T2, kShift + kSize, size2, U>;

            static constexpr bool is_valid(T value) {
                return (static_cast<U>(value) & ~static_cast<U>(kMax)) == 0;
            }

            static constexpr U encode(T value) {
                DCHECK(1);
                return static_cast<U>(value) << kShift;
            }

            JSASM_NODISCARD static constexpr U update(U previous, T value) {
                return (previous & ~kMask) | encode(value);
            }

            static constexpr T decode(U value) {
                return static_cast<T>((value & kMask) >> kShift);
            }
    };

    template <class T, int shift, int size>
    using BitField8 = BitField<T, shift, size, uint8_t>;

    template <class T, int shift, int size>
    using BitField16 = BitField<T, shift, size, uint16_t>;

    template <class T, int shift, int size>
    using BitField64 = BitField<T, shift, size, uint64_t>;
    
} // namespace base

#endif // __JSASM_BASE_FIELD_H__